/**
 * This file is part of routerdns.
 * 
 * AUTHORS
 *     Oliver Mader  <dotb52@gmail.com>
 * 
 * Copyright (C) 2009  Oliver Mader
 * 
 * Routerdns is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Routerdns is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with routerdns.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "net.h"

#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>

#include "log.h"

#ifndef NO_SSL
	#include "ssl.h"
#endif

static int net_connect(const char *host, unsigned port);
static void net_request(routerdns_t *rdns, char *buffer, unsigned size);
static char *net_base64(routerdns_t *rdns);

/* request an ip update */
extern int net_update_ip(routerdns_t *rdns)
{
	/* create a new tcp socket */
	#ifndef NO_SSL
		int sock = net_connect("members.dyndns.org", 443);
	#else
		int sock = net_connect("members.dyndns.org", 80);
	#endif
	
	if (sock == NET_ERROR)
		return NET_ERROR;
	
	#ifndef NO_SSL
		if (ssl_init(sock) == SSL_ERROR)
			return NET_ERROR;
	#endif
	
	/* the request string */
	char buffer[4096];
	net_request(rdns, buffer, 4096);
	
	printf("Request: %s", buffer);
/*
	 send our request 
	send(sock, buffer, strlen(buffer), 0);
	
	while (1) {
		int size = read(sock, buffer, 4096);
		
		if (size <= 0)
			break;
		
		printf("%*s", size, buffer);
	}
	*/
	
	/* free all used resources */
	#ifndef NO_SSL
		ssl_free();
	#endif

	close(sock);
}

/* get the current ip */
extern int net_current_ip(char *ip)
{
	/* create a new socket */
	int sock = net_connect("checkip.dyndns.com", 80);
	
	if (sock == NET_ERROR)
		return NET_ERROR;
	
	char request[] = "GET /index.html HTTP/1.1\r\nHost: checkip.dyndns.com\r\n\r\n";
	send(sock, request, strlen(request), 0);
	
	printf("Request: %s", request);
	
	char buffer[512];
	read(sock, buffer, 512);
	
	printf("Response: %s", buffer);
	
	close(sock);
	
	char *new_ip = strstr(buffer, "Address: ");
	
	if (new_ip == NULL)
		return NET_ERROR;
	
	new_ip += 9;
	
	for (int i = 0; i < 16; ++i) {
		if (new_ip[i] != '<') {
			ip[i] = new_ip[i];
		} else {
			ip[i] = '\0';
			break;
		}
	}
	
	printf("Current ip: %s\n", ip);
}

/* create a new tcp connection */
static int net_connect(const char *host, unsigned port)
{
	/* dns lookup */
	struct hostent *server_host = gethostbyname(host);
	
	if (server_host == NULL) {
		log_error("Unable to resolve DNS entry for %s: %s", host,
			strerror(errno));
		return NET_ERROR;
	}
	
	struct sockaddr_in server;
	memset(&server, 0, sizeof(struct sockaddr_in));
	
	server.sin_family = AF_INET;
	server.sin_port = htons(port);
	
	/* set the ip */
	memcpy(&(server.sin_addr), server_host->h_addr_list[0],
		server_host->h_length);
	
	/* create a new tcp socket */
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	
	if (sock < 0) {
		log_error("Unable to create a TCP socket: %s", strerror(errno));
		return NET_ERROR;
	}
	
	/* connect the socket */
	if (connect(sock, (struct sockaddr *) &server, sizeof(server))
		== -1)
	{
		log_error("Failed to connect to the update server: %s",
			strerror(errno));
		close(sock);
		return NET_ERROR;
	}
	
	return sock;
}

/* build the http request header */
static void net_request(routerdns_t *rdns, char *buffer, unsigned size)
{
	char *readable[3] = {"NO", "YES", "NOCHG"};
	
	int position = snprintf(buffer, size, "GET /nic/update?hostname=%s"
		"&wildcard=%s&mx=%s&backmx=%s&offline=%s", rdns->hostname,
		readable[rdns->wildcard], (rdns->mx == NULL) ? "NOCHG" : rdns->mx,
		readable[rdns->backmx], readable[rdns->offline]);
	
	if (rdns->ip != NULL)
		position += snprintf(buffer + position, size - position, "&myip=%s",
			rdns->ip);
	
	char *auth = net_base64(rdns);
	
	position += snprintf(buffer + position, size - position, " HTTP/1.0\r\n"
		"Host: members.dyndns.org\r\nAuthorization: Basic %s\r\n"
		"User-Agent: ASUS - WL-500W - 1.0\r\n\r\n", auth);
	
	free(auth);
}

static char *net_base64(routerdns_t *rdns)
{
	static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
		"wxyz0123456789+/";

	unsigned length = strlen(rdns->username) + strlen(rdns->password) + 1;
	
	char in[length + 1];
	snprintf(in, length, "%s:%s", rdns->username, rdns->password);
	
	char *out = malloc(length / 3 * 4 + 5);
	char *p = out;
	
	for (int i = 0; i <= length; i += 3) {
		*p++ = cb64[in[i] >> 2];
		*p++ = cb64[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xf0) >> 4)];
		*p++ = (unsigned char) (length - i > 1 ? cb64[((in[i + 1] & 0x0f) << 2)
			| ((in[i + 2] & 0xc0) >> 6)] : '=');
		*p++ = (unsigned char) (length - i > 2 ? cb64[in[i + 2] & 0x3f ] : '=');
	}
	
	*p = '\0';
	
	return out;
}
